package com.ssy.lingxi.product.serviceImpl.platform;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.vo.PageVO;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.product.api.model.response.BrandResponse;
import com.ssy.lingxi.product.common.tree.AttributeNode;
import com.ssy.lingxi.product.entity.Brand;
import com.ssy.lingxi.product.entity.commodity.Commodity;
import com.ssy.lingxi.product.entity.customer.CustomerAttribute;
import com.ssy.lingxi.product.entity.customer.CustomerAttributeValue;
import com.ssy.lingxi.product.entity.customer.CustomerCategory;
import com.ssy.lingxi.product.entity.platform.Attribute;
import com.ssy.lingxi.product.entity.platform.AttributeGroup;
import com.ssy.lingxi.product.entity.platform.AttributeValue;
import com.ssy.lingxi.product.entity.platform.Category;
import com.ssy.lingxi.product.model.request.CommodityAttributeRequest;
import com.ssy.lingxi.product.model.request.CommodityGoodsAttributeRequest;
import com.ssy.lingxi.product.model.resp.CommodityAttributeAndBrandResponse;
import com.ssy.lingxi.product.model.resp.CommodityAttributeResponse;
import com.ssy.lingxi.product.model.resp.CommodityGoodsAttributeAndBrandResponse;
import com.ssy.lingxi.product.model.resp.CommodityGoodsAttributeResponse;
import com.ssy.lingxi.product.repository.commodity.CommodityRepository;
import com.ssy.lingxi.product.repository.customer.CustomerAttributeRepository;
import com.ssy.lingxi.product.repository.customer.CustomerCategoryRepository;
import com.ssy.lingxi.product.repository.platform.AttributeGroupRepository;
import com.ssy.lingxi.product.repository.platform.AttributeRepository;
import com.ssy.lingxi.product.repository.platform.AttributeValueRepository;
import com.ssy.lingxi.product.repository.platform.CategoryRepository;
import com.ssy.lingxi.product.service.IBrandService;
import com.ssy.lingxi.product.service.ICommodityService;
import com.ssy.lingxi.product.service.customer.ICustomerAttributeService;
import com.ssy.lingxi.product.service.platform.IAttributeService;
import lombok.SneakyThrows;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.persistence.criteria.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 属性实现类
 * @author jw.chen
 * @version 2.0.0
 * @date 2020/6/23
 */
@Service
public class AttributeServiceImpl implements IAttributeService {

    @Autowired
    private AttributeRepository attributeRepository;

    @Autowired
    private AttributeGroupRepository attributeGroupRepository;

    @Autowired
    private CustomerAttributeRepository customerAttributeRepository;

    @Autowired
    private AttributeValueRepository attributeValueRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    @Resource
    private CustomerCategoryRepository customerCategoryRepository;

    @Resource
    private ICustomerAttributeService customerAttributeService;

    @Resource
    private ICommodityService commodityService;

    @Resource
    private CommodityRepository commodityRepository;

    @Resource
    private IBrandService brandService;

    /**
     * 查询属性树
     * @return Wrapper<?>
     */
    @Override
    public List<AttributeNode> getAttributeTree(Boolean filterInput){
        //一次性查出所有数据
        List<Attribute> attributeList;
        if(filterInput != null && filterInput){
           attributeList = attributeRepository.findByIsEnableAndTypeNot(true, 3);
        }else{
           attributeList = attributeRepository.findByIsEnable(true);
        }
        List<AttributeGroup> attributeGroupList = attributeGroupRepository.findAll();

        List<AttributeNode> nodeList = new ArrayList<>();
        attributeGroupList.forEach(attributeGroup -> {
            Long id = attributeGroup.getId();
            AttributeNode node = new AttributeNode();
            node.setId("g_" + id);
            node.setTitle(attributeGroup.getName());
            List<AttributeNode> childrenNodeList = treeChildren(attributeList, id);
            if(!childrenNodeList.isEmpty()){
                node.setChildren(childrenNodeList);
                nodeList.add(node);
            }
        });
        return nodeList;
    }

    /**
     * 生成子树
     * @param attributeList
     * @param id
     * @return
     */
    private List<AttributeNode> treeChildren(List<Attribute> attributeList, Long id) {
        List<AttributeNode> nodeList = new ArrayList<>();
        attributeList.forEach(attribute -> {
            Long parentId = attribute.getGroupId();
            if(id.longValue() == parentId.longValue()){
                AttributeNode node = new AttributeNode();
                node.setId("a_" + attribute.getId());
                node.setTitle(attribute.getName());
                node.setType(0);
                Boolean isDate = attribute.getIsDate();
                if(isDate != null && isDate){
                    node.setType(1);
                }
                Boolean isArea = attribute.getIsArea();
                if(isArea != null && isArea){
                    node.setType(2);
                }
                nodeList.add(node);
            }
        });
        return nodeList;
    }

    /**
     * 查询属性
     * @param pageVO
     * @param isEnable
     * @param groupName
     * @param name
     * @return Page<Attribute>
     */
    @Override
    public Page<Attribute> getAttributeList(PageVO pageVO, Boolean isEnable, String groupName, String name){
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize());

        List<Boolean> enaList = isEnable == null ? Stream.of(true, false).collect(Collectors.toList()) : Stream.of(isEnable).collect(Collectors.toList());
        if(StringUtils.isEmpty(groupName)){
            return attributeRepository.findByNameContainingAndIsEnableInOrderByIdDesc(name, enaList, page);
        }else{
            return attributeRepository.findByGroupNameContainingAndNameContainingAndIsEnableInOrderByIdDesc(groupName, name, enaList, page);
        }

    }

    /**
     * 查询属性
     * @return id
     */
    @Override
    public Attribute getAttribute(Long id){
        return attributeRepository.findById(id).orElse(null);
    }

    /**
     * 添加/修改属性
     * @param attribute
     * @return
     * @throws Exception
     */
    @Override
    public String saveOrUpdateAttribute(Attribute attribute){
        Long id = attribute.getId();
        String name = attribute.getName();
        List<Attribute> old_attribute;
        //判断属性名是否重复
        if(id != null && id > 0){
            old_attribute = attributeRepository.findByNameAndIdNot(name, id);
        }else{
            old_attribute = attributeRepository.findByName(name);
        }
        if(!old_attribute.isEmpty()){
            throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NAME_EXIST);
        }
        //判断属性组名是否存在，不存在则新增
        String groupName = attribute.getGroupName();
        AttributeGroup attributeGroup = attributeGroupRepository.findFirstByName(groupName);
        if(attributeGroup == null){
            attributeGroup = new AttributeGroup();
            attributeGroup.setName(groupName);
            attributeGroupRepository.saveAndFlush(attributeGroup);
        }
        attribute.setGroupId(attributeGroup.getId());
        //数据库持久化对象
        attributeRepository.saveAndFlush(attribute);
        return ResponseCode.SUCCESS.getMessage();
    }

    /**
     * 删除属性
     * @param id
     * @return
     */
    @Override
    public String deleteAttribute(Long id) {
        //验证数据库中是否存在该数据
        Attribute attribute = attributeRepository.findById(id).orElse(null);
        if(attribute != null){
            Boolean isEnable = attribute.getIsEnable();
            if(!isEnable){
                attributeRepository.deleteById(id);
                return ResponseCode.SUCCESS.getMessage();
            }else{
                throw new BusinessException(ResponseCode.PRODUCT_ENABLE_NOT_DELETE);
            }
        }
        throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_EXIST);
    }

    /**
     * 启用/停用属性
     * @param id
     * @param isEnable
     * @return
     */
    @Override
    public String updateAttributeStatus(Long id, Boolean isEnable){
        //验证数据库中是否存在该数据
        Attribute attribute = attributeRepository.findById(id).orElse(null);
        if(attribute != null){
            if(!isEnable){
                //判断是否存在品类关联
                List<Category> categoryList = categoryRepository.findByAttributeListId(attribute.getId());
                if(categoryList != null && categoryList.size() > 0){
                    throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_HAVE_CATEGORY_NOT_DELETE);
                }
                //判断是否存在属性值
                List<AttributeValue> attributeValueList = attributeValueRepository.findByAttributeId(id);
                if(!attributeValueList.isEmpty()){
                    throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_VALUE_NOT_STOP);
                }
                //判断会员属性是否有关联
                List<CustomerAttribute> customerAttributeList = customerAttributeRepository.findByAttributeId(id);
                if(!customerAttributeList.isEmpty()){
                    throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_STOP);
                }
            }
            //持久化数据
            attribute.setIsEnable(isEnable);
            attributeRepository.saveAndFlush(attribute);
            return ResponseCode.SUCCESS.getMessage();
        }else{
            throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_EXIST);
        }
    }

    /**
     * 获取可选的属性
     *
     * @param commodityAttributeRequest
     * @return
     */
    @Override
    @SneakyThrows
    public CommodityAttributeAndBrandResponse getEffectiveAttribute(CommodityAttributeRequest commodityAttributeRequest) {

        //查询会员属性
        List<CustomerAttribute> customerAttributeList = customerAttributeRepository.findAll(getCustomerAttributeSpecification(commodityAttributeRequest));

        //获取会员属性id
        List<Long> customerAttributeIdList = getCustomerAttributeIdList(customerAttributeList);

        List<BrandResponse> brandResponses = getBrandByCategoryId(commodityAttributeRequest);

        //从品牌参数中获取相关的会员属性id
        List<Long> gainCustomerAttributeIdList = getAttributeIdFromBrand(brandResponses);

        //从品牌的得到的会员属性id并进传参
        customerAttributeIdList = setSearchParam(customerAttributeIdList,gainCustomerAttributeIdList);

        //获取会员属性值id
        List<Long> customerAttributeValueIdList = getCustomerAttributeValueIdList(customerAttributeList);

        //获取会员属性的可用属性
        CompletableFuture<CommodityGoodsAttributeAndBrandResponse> completableFuture = getEffectiveAttributeList(customerAttributeIdList, customerAttributeValueIdList, commodityAttributeRequest.getCategoryId());

        CommodityGoodsAttributeAndBrandResponse commodityGoodsAttributeAndBrandResponse = completableFuture.get();
        List<CommodityGoodsAttributeResponse> effectiveAttributeList = commodityGoodsAttributeAndBrandResponse.getCommodityGoodsAttributeResponseList();

        //获取可用会员属性的id
        List<Long> effectiveAttributeIdList = getEffectiveAttributeIdList(effectiveAttributeList);

        //获取可用的品牌
        List<Long> brandIdList = getEffectiveBrandId(effectiveAttributeIdList, commodityAttributeRequest.getCategoryId());

        //获取完整可用会员属性
        customerAttributeList = customerAttributeRepository.findByIdIn(effectiveAttributeIdList);

        Map<Long, List<Long>> effectiveAttributeMap = effectiveAttributeList
                .stream()
                .collect(Collectors.toMap(CommodityGoodsAttributeResponse::getCustomerAttributeId,
                        CommodityGoodsAttributeResponse::getCustomerAttributeValueId));


        return getResponse(customerAttributeList, brandIdList, effectiveAttributeMap);
    }

    private CommodityAttributeAndBrandResponse getResponse(List<CustomerAttribute> customerAttributeList, List<Long> brandIdList, Map<Long, List<Long>> effectiveAttributeMap) {
        CommodityAttributeAndBrandResponse commodityAttributeAndBrandResponse = new CommodityAttributeAndBrandResponse();
        List<CommodityAttributeResponse> commodityAttributeResponseList = getAttributeResponse(customerAttributeList, effectiveAttributeMap);
        commodityAttributeAndBrandResponse.setCommodityAttributeResponseList(commodityAttributeResponseList);
        commodityAttributeAndBrandResponse.setBrandIdList(brandIdList);
        return commodityAttributeAndBrandResponse;
    }


    private List<CommodityAttributeResponse> getAttributeResponse(List<CustomerAttribute> customerAttributeList, Map<Long, List<Long>> effectiveAttributeMap) {
        return new ArrayList<>(customerAttributeList
                .stream()
                .filter(this::distinctAttributeId)
                .filter(customerAttribute -> isContainsKey(effectiveAttributeMap, customerAttribute))
                .map(customerAttribute -> compareAndSet(effectiveAttributeMap, customerAttribute))
                .collect(Collectors.toMap(CommodityAttributeResponse::getAttributeId, Function.identity(), this::distinctAndSum))
                .values());
    }


    @SneakyThrows
    private List<Long> getEffectiveBrandId(List<Long> effectiveAttributeIdList, Long categoryId) {
        CompletableFuture<List<Long>> completableFuture = getBrandIdList(effectiveAttributeIdList, categoryId);
        List<Long> brandIdList = completableFuture.get();
        brandIdList = brandIdList
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        return brandIdList;
    }

    @Async
    protected CompletableFuture<List<Long>> getBrandIdList(List<Long> effectiveAttributeIdList,Long categoryId) {
        List<Long> customerCategoryIdList = getCustomerCategoryIdList(categoryId);
        List<Long> brandIdList = commodityRepository.findBrandIdListWithCondition(effectiveAttributeIdList,customerCategoryIdList);
        return CompletableFuture.completedFuture(brandIdList);
    }

    private List<Long> getCustomerCategoryIdList(Long categoryId) {
        List<CustomerCategory> customerCategoryList = customerCategoryRepository.findByCategoryId(categoryId);
        return customerCategoryList
                .stream()
                .filter(Objects::nonNull)
                .map(CustomerCategory::getId)
                .distinct()
                .collect(Collectors.toList());
    }


    /**
     * 从品牌的得到的会员属性id并进传参
     *
     * @param customerAttributeIdList
     * @param gainCustomerAttributeIdList
     * @return
     */
    private List<Long> setSearchParam(List<Long> customerAttributeIdList, List<Long> gainCustomerAttributeIdList) {
        if (CollectionUtil.isEmpty(customerAttributeIdList)) {
            return gainCustomerAttributeIdList;
        } else {
            HashSet idSet = new HashSet();
            idSet.addAll(customerAttributeIdList);
            idSet.addAll(gainCustomerAttributeIdList);
            return new ArrayList<>(idSet);
        }
    }


    /**
     * 从品牌参数中获取相关的会员属性id
     *
     * @param brandResponseList
     * @return
     */
    private List<Long> getAttributeIdFromBrand(List<BrandResponse>  brandResponseList) {
        return brandResponseList
                .stream()
                .map(BrandResponse::getId)
                .distinct()
                .collect(Collectors.toList());
    }

    private List<BrandResponse> getBrandByCategoryId(CommodityAttributeRequest commodityAttributeRequest) {
        List<Commodity> commodityList = getCommodityByCategoryId(commodityAttributeRequest.getCategoryId());
        return getBrandList(commodityList);
    }

    /**
     * 获取品牌
     *
     * @param commodityList
     * @return
     */
    private List<BrandResponse> getBrandList(List<Commodity> commodityList) {
        return commodityList
                .stream()
                .filter(commodity -> Objects.nonNull(commodity.getBrand()))
                .map(commodity -> {
            BrandResponse brandResponse = new BrandResponse();
            Brand brand = commodity.getBrand();
            BeanUtil.copyProperties(brand, brandResponse);
            return brandResponse;
        }).collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                new TreeSet<>(Comparator.comparing(BrandResponse::getId))), ArrayList::new));
    }

    /**
     * 根据平台品类获取商品
     *
     * @param categoryId
     * @return
     */
    private List<Commodity> getCommodityByCategoryId(Long categoryId) {
        return commodityService.findAllByCategoryIdIn(new ArrayList<Long>() {{
            add(categoryId);
        }});
    }

    /**
     * compareAndSet
     *
     * @param effectiveAttributeMap
     * @param customerAttribute
     * @return
     */
    private CommodityAttributeResponse compareAndSet(Map<Long, List<Long>> effectiveAttributeMap, CustomerAttribute customerAttribute) {
        List<Long> attributeValueIdList = effectiveAttributeMap.get(customerAttribute.getId());
        List<CustomerAttributeValue> customerAttributeValueList = customerAttribute.getCustomerAttributeValueList();
        CommodityAttributeResponse commodityAttributeResponse = new CommodityAttributeResponse();
        if (Objects.nonNull(customerAttribute.getAttribute())) {
            commodityAttributeResponse.setAttributeId(customerAttribute.getAttribute().getId());
        }
        List<Long> tempAttributeValueIdList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(attributeValueIdList) && CollectionUtil.isNotEmpty(customerAttributeValueList)) {
            attributeValueIdList.forEach(attributeValueId -> {
                customerAttributeValueList.forEach(customerAttributeValue -> {
                    if (attributeValueId.equals(customerAttributeValue.getId())) {
                        if (Objects.nonNull(customerAttributeValue.getAttributeValue())) {
                            tempAttributeValueIdList.add(customerAttributeValue.getAttributeValue().getId());
                        }
                    }
                });
            });
            commodityAttributeResponse.setAttributeValueId(tempAttributeValueIdList);
        } else {
            commodityAttributeResponse.setAttributeValueId(Lists.newArrayList());
        }
        return commodityAttributeResponse;
    }

    /**
     * 判断map中是否包含key
     *
     * @param effectiveAttributeMap
     * @param customerAttribute
     * @return
     */
    private boolean isContainsKey(Map<Long, List<Long>> effectiveAttributeMap, CustomerAttribute customerAttribute) {
        return effectiveAttributeMap.containsKey(customerAttribute.getId());
    }

    /**
     * customerAttributeId去重
     *
     * @param customerAttribute
     * @return
     */
    private boolean distinctAttributeId(CustomerAttribute customerAttribute) {
        ConcurrentHashMap<Object, Object> concurrentHashMap = new ConcurrentHashMap<>();
        return concurrentHashMap.putIfAbsent(customerAttribute.getId(), Boolean.TRUE) == null;
    }


    /**
     * 获取可用会员属性的id
     *
     * @param effectiveAttributeList
     * @return
     */
    private List<Long> getEffectiveAttributeIdList(List<CommodityGoodsAttributeResponse> effectiveAttributeList) {
        return effectiveAttributeList.stream()
                .map(CommodityGoodsAttributeResponse::getCustomerAttributeId)
                .collect(Collectors.toList());
    }

    /**
     * 去重合并属性值
     *
     * @param o1
     * @param o2
     * @return
     */
    private CommodityAttributeResponse distinctAndSum(CommodityAttributeResponse o1, CommodityAttributeResponse o2) {
        HashSet<Long> attributeValueSet = new HashSet<>();
        if (Objects.nonNull(o1.getAttributeValueId())) {
            attributeValueSet.addAll(o1.getAttributeValueId());
        }
        if (Objects.nonNull(o2.getAttributeValueId())) {
            attributeValueSet.addAll(o2.getAttributeValueId());
        }
        o1.setAttributeValueId(new ArrayList<>(attributeValueSet));
        return o1;
    }

    /**
     * 获取会员属性值id集合
     *
     * @param customerAttributeList
     * @return
     */
    private List<Long> getCustomerAttributeValueIdList(List<CustomerAttribute> customerAttributeList) {
        return customerAttributeList
                .stream()
                .map(CustomerAttribute::getCustomerAttributeValueList)
                .filter(CollectionUtil::isNotEmpty)
                .flatMap(List::stream)
                .map(CustomerAttributeValue::getId)
                .collect(Collectors.toList());
    }

    /**
     * 获取会员属性id集合
     *
     * @param customerAttributeList
     * @return
     */
    private List<Long> getCustomerAttributeIdList(List<CustomerAttribute> customerAttributeList) {
        return customerAttributeList
                .stream()
                .map(CustomerAttribute::getId)
                .collect(Collectors.toList());
    }


    /**
     * 获取会员属性的可用属性
     *
     * @param customerAttributeIdList
     * @param customerAttributeValueIdList
     * @return
     */
    @Async
    protected CompletableFuture<CommodityGoodsAttributeAndBrandResponse> getEffectiveAttributeList(List<Long> customerAttributeIdList, List<Long> customerAttributeValueIdList, Long categoryId) {
        CommodityGoodsAttributeRequest commodityGoodsAttributeRequest = new CommodityGoodsAttributeRequest();
        commodityGoodsAttributeRequest.setAttributeIdList(customerAttributeIdList);
        commodityGoodsAttributeRequest.setAttributeValueIdList(customerAttributeValueIdList);
        List<Long> customerCategoryIdList = getCustomerCategoryIdList(categoryId);
        commodityGoodsAttributeRequest.setCategoryIdList(customerCategoryIdList);
        return CompletableFuture.completedFuture(customerAttributeService.getEffectiveAttribute(commodityGoodsAttributeRequest));
    }

    private Specification<CustomerAttribute> getCustomerAttributeSpecification(CommodityAttributeRequest commodityAttributeRequest) {
        return (root, query, criteriaBuilder) -> {
            List<Long> attributeIdList = commodityAttributeRequest.getAttributeIdList();
            List<Long> attributeValueIdList = commodityAttributeRequest.getAttributeValueIdList();
            Predicate finalConditions = criteriaBuilder.conjunction();
            if (CollectionUtil.isNotEmpty(attributeIdList)) {
                finalConditions = criteriaBuilder.and(finalConditions, criteriaBuilder.in(root
                                .join("attribute", JoinType.LEFT)
                                .get("id"))
                        .value(attributeIdList)
                );
            }
            if (CollectionUtil.isNotEmpty(attributeValueIdList)) {
                finalConditions = criteriaBuilder.and(finalConditions, criteriaBuilder.in(root
                        .join("attribute", JoinType.LEFT)
                        .join("attributeValueList", JoinType.LEFT)
                        .get("id"))
                        .value(attributeValueIdList));
            }
            return query.where(finalConditions).getRestriction();
        };
    }

}

